Go Programming - Basics

Concepts of Programming Languages

Sebastian Macke

Rosenheim Technical University

Last Lecture:

-- Miro Board ....

2

Characteristics of a Successful Language (Suggestion)

3

What is an Orthogonal language

4

Orthogonal Design by an example

5

Types VS. Functions (Non-Orthogonal Design by example)

// Ints are allowed to return
int returnint() {
    return 1;
}

// Structs are allowed to return
typedef struct{} structdef;
structdef returnstruct() {
    structdef mystruct;
    return mystruct;
}

// Arrays are not allowed to return
int[3] returnarray() {
    static int myarray[3] = {1, 2, 3};
    return myarray;
}
6

Other non-orthogonal examples

-- Go doesn't allow this operation.

-- In Go, there is only one loop: for

int x; // x is a value
MyClass y; // y is a reference

-- Go has pointers and values. But the way they are stored is the same.

7

Golang

8

Golang

9

Tools

go env Print Go environment information

go run file.go just runs the code. Can be multiple files, too.

go build downloads dependencies and builds the executable, but needs a go.mod file.

go tool dist list list all supported platforms

env GOOS=darwin GOARCH=arm64 go build Build for Apple ARM CPU computers.

go fmt formats your files

go mod init creates a go.mod module file

go mod tidy updates the go mod file if you add more dependencies

10

Packages

package main

import (
    "fmt"
)

func main() {
    fmt.Printf("Hello %s", "Programming with Go \xE2\x98\xAF\n") // \xE2\x98\xAF -> ☯
    fmt.Printf("Hello %s", "Programming with Go ☯\n")
}
11

Primitive Types

12

Primitive Types

Golang Java Comment
bool boolean
string String
int machine-sized in Go
int8 byte
int16 short
int32 int
int64 long
uint - unsigned int
uint8 -
uint16 -
uint32 -
uint64 -
uintptr -
byte - alias for uint8
rune char alias for int32, represents a Unicode code point. In Java usually 2 bytes.
float32 float
float64 double
complex64 // complex
complex128
struct {x,z,y int} class
13

Complex Numbers

package main

import (
    "fmt"
    "math/cmplx"
)

func main() {
    c := 3 + 4i
    fmt.Printf("c=%v\n", c)
    r, θ := cmplx.Polar(c) // Go supports variables with unicode characters
    fmt.Printf("r=%v, θ=%v\n", r, θ)
}
14

Weak typing vs. Strong typing I

Languages differ of how serious they handle the type

15

Weak typing vs. Strong typing II

Go uses a strong typing system. But why?

16

Where implicit type conversion in C goes wrong

int main() {
    signed int a = -1;
    unsigned int b = 0;

    if (a < b) {
        printf("a ís smaller than b\n");
    }

    return 0;
}

Also: Change int to char

17

Javascript is very creative with type coercion

18

Dynamically typing in Python

19

Strong typing in Go

package main

import "fmt"

func main() {
    var a int32 = -1
    var b uint32 = 2

    if a < int32(b) {
        fmt.Printf("a is smaller than b")
    }
}
20

The many ways of variable declarations in Go

var foo int  // default value zero
foo = 32

var foo int = 32

var foo = 32

foo := 32  // infers (guesses) the type definition of type int

foo := int(32)

foo, ok := 32, true // foo is an int 32, ok is a boolean with value true
21

Go has also a dynamic type: any

func PrintVariableDetails(v any) {
    typeof := reflect.TypeOf(v)
    fmt.Printf("The variable with type '%s' has the value '%v'\n", typeof.Name(), v)
}

func main() {
    var someValue any
    someValue = 2
    PrintVariableDetails(someValue)

    someValue = "abcd"
    PrintVariableDetails(someValue)

    if tmp, ok := someValue.(string); ok {
        fmt.Println("someValue is a string and has the value", tmp)
    }
}
22

Type systems in other languages

23

Golang - some details

24

Maps

package main

import "fmt"

func main() {
    m := make(map[string]string) // Initialize an empty map
    m["foo"] = "bar"             // insert a key-value pair into map

    value, ok := m["asd"]  // check if key is present, return parameters can be ignored with "_"
    fmt.Println(ok, value) // returns false, value is just an empty string ""

    value, ok = m["foo"] // returns true, value contains "bar"
    fmt.Println(ok, value)

    for k, v := range m {
        fmt.Println(k, v)
    }
}
25

Arrays and Slices

26

Arrays and Slices

package main

import "fmt"

func main() {
    arr := [5]int{1, 2, 3, 4, 5} // create fixed array
    fmt.Println(arr)

    s := arr[0:2] // create a slice out of the array. Otherwise via "make([2]int)" for a new array
    fmt.Println(s)

    fmt.Println(len(s), cap(s)) // ==> 2, 5
    s[0] = 100                  // a slice is just a reference
    fmt.Println(arr)

    s = append(s, 8)
    s = append(s, 9)
    s = append(s, 10)
    s = append(s, 11)

    fmt.Println(len(s), cap(s)) // ==> 6, 10
    fmt.Println(arr)
}
27

Functions and Control Structures: Example Palindrome

// IsPalindrome implementation. Does only work for 1-Byte UTF-8 chars (ASCII).
func IsPalindrome(word string) bool {
    for pos := 0; pos < len(word)/2; pos++ {
        if word[pos] != word[len(word)-pos-1] {
            return false
        }
    }
    return true
}
28

Go directly supports Unit Tests via "go test"

// palindrome_test.go
func TestPalindrome(t *testing.T) {
    if !IsPalindrome("") {
        t.Error("isPalindrome('' should be true. But is false.")
    }
    if !IsPalindrome("o") {
        t.Error("isPalindrome('o' should be true. But is false.")
    }
    if !IsPalindrome("oto") {
        t.Error("isPalindrome('oto' should be true. But is false.")
    }
    if IsPalindrome("ottos") {
        t.Error("isPalindrome('ottos' should be false. But is true.")
    }
}
29

Functions and Control Structures: Example Palindrome (UTF-8)

// IsPalindrome2 is using runes. This works for all UTF-8 chars (SBC, MBC).
func IsPalindrome2(word string) bool {
    var runes = []rune(word)
    for pos, ch := range runes {
        if ch != runes[len(runes)-pos-1] {
            return false
        }
    }
    return true
}
pos, ch := range runes
30

Functions and Control Structures: Example Palindrome (Reverse)

// IsPalindrome3 is implemented by reusing Reverse(). Reverse works for UTF-8 chars.
func IsPalindrome3(word string) bool {
    return strings.Reverse(word) == word
}
31

Pointers

32

Pointer I

A pointer is a variable which contains a memory address

func main() {
    num := 42   // implicit type "int" with value 42
    ptr := &num // implicit type of "pointer to type int points to num"
    //var num int = 42 // explicit type definition
    //var ptr *int = &num // explicit type definition

    fmt.Println("Value of num:", num)
    // Expected Output: Value of num: 42

    fmt.Println("Address of num:", &num)
    // Expected Output: Address of num: 0xSOME_MEMORY_ADDRESS (this will vary every run)

    fmt.Println("Address stored in ptr:", ptr)
    // Expected Output: Address stored in ptr: 0xSOME_MEMORY_ADDRESS (same as address of num)

    fmt.Println("Value via pointer:", *ptr)
    // Expected Output: Value via pointer: 42

    *ptr = 43
    fmt.Println("Value of num:", num)
    // Expected output: New value in num = 43
}
33

Pointer I

func swap0(x, y int) (int, int) {
    return y, x
}
func swap1(x, y int) {
    x, y = y, x
}
func swap2(x *int, y *int) {
    *x, *y = *y, *x
}
func swap3(x **int, y **int) {
    *x, *y = *y, *x
}
34

Pointer II

func main() {
    var a, b = 1, 2
    fmt.Printf("Initial : a=%d, b=%d\n", a, b)

    a, b = b, a
    fmt.Printf("After a,b = b,a : a=%d, b=%d\n", a, b)

    swap0(a, b)
    fmt.Printf("After swap0(a,b) : a=%d, b=%d\n", a, b)

    a, b = swap0(a, b)
    fmt.Printf("After a,b = swap0(a,b) : a=%d, b=%d\n", a, b)

    swap1(a, b)
    fmt.Printf("After swap1(a,b) : a=%d, b=%d\n", a, b)

    swap2(&a, &b)
    fmt.Printf("After swap2(&a,&b) : a=%d, b=%d\n", a, b)

    pa, pb := &a, &b
    swap3(&pa, &pb)
    fmt.Printf("After swap3(&pa, &pb): a=%d, b=%d, *pa=%v, *pb=%v\n", a, b, *pa, *pb)
}
35

Exercise 2.1

36

Pointer arithmetic is a "DON'T DO" in Go!

func main() {
    z := [2]int64{10, 20} // fixed size array
    y := &z[0]            // pointer to first entry in array
    fmt.Println(y)
    y = (*int64)(unsafe.Add(unsafe.Pointer(y), 8)) // 64 Bit has 8 bytes
    fmt.Println(y)
    *y = 30 // changes second parameter in the array z
    fmt.Println(z)
}
37

Maps and Slices - Example Book Index

Types can be defined with the keyword "type"

// Page contains an array of words.
type Page []string

// Book is an array of pages.
type Book []Page

// Index contains a list of pages for each word in a book.
type Index map[string][]int

// MakeIndex generates an index structure
func MakeIndex(book Book) Index {
....
}
38

Stringer Interface

package main

import (
    "fmt"
    "strings"
)

type Page []string

func (p Page) String() string {
    return strings.Join(p, "\n")
}

func main() {
    page := Page{"This is the first paragraph", "This is the second paragraph"}
    fmt.Println(page)
}
39

The Flag API simplifies Command Line Utilities

import (
    "flag"
    "fmt"
)

// Simple test for the Go flag API.
func main() {
    // construct a string flag with a default ip address and a description.
    ip := flag.String("ip", "192.168.1.1", "Overrides the default IP address.")
    port := flag.String("port", "8080", "Overrides the default listen port.")
    flag.Parse()

    fmt.Println("\nDefault value for IP: " + *ip)
    fmt.Println("\nDefault value for port: " + *port)
}
40

Summary

41

Exercise 2.2

42

Thank you

Sebastian Macke

Rosenheim Technical University

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)